SpringSecurity 工具类 BCryptPasswordEncoder 是如何加密解密的
SpringSecurity 中 PasswordEncoder 的加密是采用 SHA-256 + 随机盐 + 密钥进行加密,那解密时如何解密的?
如下实例:
public class BCryptPasswordEncoderTest {
public static void main(String[] args) {
String pass = "admin";
BCryptPasswordEncoder bcryptPasswordEncoder = new BCryptPasswordEncoder();
String hashPass = bcryptPasswordEncoder.encode(pass);
System.out.println(hashPass);
boolean f = bcryptPasswordEncoder.matches("admin", hashPass);
System.out.println(f);
}
}
可以看到,每次输出的 hashPass 都不一样,但是最终的 f 都为 true,即匹配成功。
查看代码,可以看到,其实每次的随机盐,都保存在 hashPass 中。
在进行 matchs 进行比较时,都会调用 BCrypt 的 String hashpw(String password, String salt)
方法。两个参数即 admin 和 hashPass
//******BCrypt.java******salt即取出要比较的DB中的密码*******
real_salt = salt.substring(off + 3, off + 25);
try {
// ***************************************************
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
}
catch (UnsupportedEncodingException uee) {}
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds);
假定一次 hashPass 为:
$2a$10$AxafsyVqK51p.s9WAEYWYeIY9TKEoG83LTEOSB3KUkoLtGsBKhCwe
随机盐即为 AxafsyVqK51p.s9WAEYWYe
,而这个随机盐,会在比较的时候,重新被取出。
补充:Spring Security 的 PasswordEncoder 可以选多种 Hash 算法,然后可以配置是否把 Hash 结果用 Base64 编码(或者用Hex十六进制表示,默认),然后得到的结果存库(假设用的 DB 存储);
然后密码匹配阶段并没有进行解密(因为是 Hash,是不可逆的,也即解不开),而是根据配置,先进行解码(注意是解码),得到 hash 值;然后使用相同的算法把现在用户输入的密码进行 hash,然后看现在的 hash 值是否与刚刚解码出来(从库中用户表读取的) hash 值相等,如果等说明用户密码正确。
这正是为什么处理密码要用 hash 算法,而不用加密,因为这样处理即使库泄漏,黑客也很难(加盐可以说几乎不可能,MD5 除外)破解密码(破解密码只能用彩虹表用相同的 hash 算法 hash 然后匹配,一个一个试)。